package com.kevin.websocket.server.configuration.websocket;

import com.kevin.websocket.server.configuration.util.JsonUtil;
import com.kevin.websocket.server.model.dto.MessageDTO;
import com.kevin.websocket.server.model.enums.PublishType;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@ServerEndpoint(value = "/ws/lbs/{userId}", configurator = WebSocketConfiguration.class)
public class WebSocketEndpoint {

    public static final String USER_ID = "USER_ID";
    public static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        // 缓存 season
        session.getUserProperties().put(USER_ID, userId);

        // TODO 上线通知，按需实现

        SESSION_MAP.put(userId, session);
        log.debug("session : {} , sessionId : {} , userId : {}  connection successful ！", session, session.getId(), userId);
    }

    /**
     * 连接关闭调用方法
     */
    @OnClose
    public void onClose(Session session) {
        Object userId = session.getUserProperties().get(USER_ID);

        // TODO 离线通知，按需实现

        Session remove = SESSION_MAP.remove(userId.toString());
        log.debug("session : {} , sessionId : {} , userId : {}  disconnect successful ！", remove, remove.getId(), userId);
    }

    /**
     * 连接错误调用方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
        Object userId = session.getUserProperties().get(USER_ID);
        if (null != userId) {
            SESSION_MAP.remove(userId.toString());
        }
        log.debug("session : {} , sessionId : {} , userId : {}  onError : {}", session, session.getId(), userId, error.getMessage(), error);
    }

    /**
     * 客户端发送消息，服务端接收消息调用方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        Object userId = session.getUserProperties().get(USER_ID);
        log.debug("session : {} , sessionId : {} , userId : {}  message : {}", session, session.getId(), userId, message);

        MessageDTO messageDTO = null;
        try {
            messageDTO = JsonUtil.jsonToPojo(message, MessageDTO.class);
            if (null != messageDTO && null != messageDTO.getPublishType()) {
                String publishType = messageDTO.getPublishType();
                switch (PublishType.valueOfVal(publishType)) {
                    case BROADCAST:
                        this.broadcast(messageDTO);
                        break;
                    case MULTICAST:
                        this.multicast(messageDTO);
                        break;
                    case UNICAST:
                        this.unicast(messageDTO);
                        break;
                }
            }
        } catch (Exception e) {
            log.warn("format message failed : {}", e.getMessage(), e);
        }
    }

    /**
     * 广播消息
     */
    public void broadcast(MessageDTO message) {
        String fromUser = message.getFrom();
        Iterator<Map.Entry<String, Session>> iterator = SESSION_MAP.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Session> next = iterator.next();
            String from = next.getKey();
            Session session = next.getValue();
            if (!from.equals(fromUser)) { // 排除自身
                if (session.isOpen()) {
                    try {
                        session.getBasicRemote().sendText(JsonUtil.objectToJson(message));
                    } catch (Exception e) {
                        log.warn("publish error : {}", e.getMessage(), e);
                    }
                } else {
                    iterator.remove();
                }
            }
        }
    }

    /**
     * 组播消息
     */
    public void multicast(MessageDTO message) {
        List<String> tos = message.getTos();
        if (!tos.isEmpty()) {
            tos.forEach(to -> {
                if (SESSION_MAP.containsKey(to)) {
                    Session session = SESSION_MAP.get(to);
                    try {
                        if (session.isOpen()) {
                            session.getBasicRemote().sendText(JsonUtil.objectToJson(message));
                        } else {
                            session.close();
                            SESSION_MAP.remove(to);
                        }
                    } catch (IOException e) {
                        log.warn("publish error : {}", e.getMessage(), e);
                    }
                }
            });
        }
    }

    /**
     * 单播消息
     */
    public void unicast(MessageDTO message) {
        if (SESSION_MAP.containsKey(message.getTos().getFirst())) {
            Session session = SESSION_MAP.get(message.getTos().getFirst());
            synchronized (session) {
                try {
                    if (session.isOpen()) {
                        session.getBasicRemote().sendText(JsonUtil.objectToJson(message));
                    } else {
                        session.close();
                        SESSION_MAP.remove(message.getTos().getFirst());
                    }
                } catch (IOException e) {
                    log.warn("publish error : {}", e.getMessage(), e);
                }
            }
        }
    }

}